package config

import (
	"context"
	"github.com/apolloconfig/agollo/v4"
	apolloconfig "github.com/apolloconfig/agollo/v4/env/config"
	"github.com/apolloconfig/agollo/v4/storage"
	"github.com/hyperway/pkg/constant"
	"github.com/hyperway/pkg/logger"
	"github.com/spf13/cast"
	"strings"
)

var apolloClient agollo.Client

// ApolloLifecycle apollo生命周期
type ApolloLifecycle int

func (l *ApolloLifecycle) Title() string {
	return "apollo"
}

// OnDestroy 关闭apollo客户端
func (l *ApolloLifecycle) OnDestroy(ctx context.Context) error {
	if apolloClient != nil {
		apolloClient.Close()
	}
	return nil
}

// NewApolloLifecycle 创建ApolloLifecycle实例
func NewApolloLifecycle() *ApolloLifecycle {
	return new(ApolloLifecycle)
}

// OnPrepare 初始化apollo客户端
func (l *ApolloLifecycle) OnPrepare() error {

	IsInitialized()
	apolloConfig := GetWrapper(constant.ApolloConfig)
	address := apolloConfig.GetString("address")
	appId := apolloConfig.GetString("app_id")
	cluster := apolloConfig.GetString("cluster")
	namespaces := apolloConfig.GetStringSlice("namespaces")

	logger.Infow("Apollo initialization parameters", "address", address, "app-id", appId, "namespaces", namespaces)

	agollo.SetLogger(logger.GetLogger())

	var err error
	apolloClient, err = agollo.StartWithConfig(func() (*apolloconfig.AppConfig, error) {
		return &apolloconfig.AppConfig{
			AppID:          appId,
			Cluster:        cluster,
			IP:             address,
			NamespaceName:  strings.Join(namespaces, ","),
			IsBackupConfig: false,
		}, nil
	})

	if err != nil {
		return err
	}

	initKV(apolloClient, namespaces)
	apolloClient.AddChangeListener(l)
	return nil
}

// initKV 初始化配置到viper
func initKV(apolloClient agollo.Client, namespaces []string) {
	viper := GlobalConfig()
	for _, ns := range namespaces {
		apolloClient.GetConfigCache(ns).Range(func(key, value interface{}) bool {
			logger.Debugw("Apollo config init", "namespace", ns, "key", key, "value", cast.ToString(value))
			viper.Set(cast.ToString(key), value)
			return true
		})
	}
}

// OnChange 配置变更事件
func (l *ApolloLifecycle) OnChange(changeEvent *storage.ChangeEvent) {
	viper := GlobalConfig()
	for k, v := range changeEvent.Changes {
		key := cast.ToString(k)
		if v.ChangeType == storage.ADDED || v.ChangeType == storage.MODIFIED {
			viper.Set(key, v.NewValue)
		} else if v.ChangeType == storage.DELETED {
			viper.Set(key, nil)
		}
		logger.Infow("Apollo config change event", "namespace", changeEvent.Namespace, "config.key", key, "config.value", v.NewValue, "config.changeType", v.ChangeType)
	}
}

func (l *ApolloLifecycle) OnNewestChange(event *storage.FullChangeEvent) {
	//write your code here
}
